//
//  YNCrashReport.m
//  CarService
//
//  Created by fengyn on 15/2/27.
//  Copyright (c) 2015年 lexingsoft. All rights reserved.
//

#import "YNCrashReport.h"
#include <libkern/OSAtomic.h>
#include <execinfo.h>

NSString * const UncaughtExceptionHandlerSignalExceptionName = @"UncaughtExceptionHandlerSignalExceptionName";
NSString * const UncaughtExceptionHandlerSignalKey = @"UncaughtExceptionHandlerSignalKey";
NSString * const UncaughtExceptionHandlerAddressesKey = @"UncaughtExceptionHandlerAddressesKey";

volatile int32_t UncaughtExceptionCount = 0;
const int32_t UncaughtExceptionMaximum = 10;

const NSInteger UncaughtExceptionHandlerSkipAddressCount = 4;
const NSInteger UncaughtExceptionHandlerReportAddressCount = 5;

NSString * const CrashDirectory = @"com.fyndev.YNCrashReport";
NSString * const CrashLogFileName = @"YNCrashLog";
@implementation YNCrashReport

// 沙盒路径
+ (NSString *)getDocumentPath
{
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documents = [paths objectAtIndex:0];
    return documents;
}

// 获取目录
+ (NSString *)getDocumentWithName:(NSString *)name
{
    if (name == nil) {
        return nil;
    }
    
    NSString *documents = [DataManager getDocumentPath];
    NSString *database_path = [documents stringByAppendingPathComponent:name];
    NSFileManager *wk_manager = [NSFileManager defaultManager];
    BOOL wk_isExist,wk_isDirectory;
    wk_isExist = [wk_manager fileExistsAtPath:database_path isDirectory:&wk_isDirectory];
    if(wk_isExist == NO || wk_isDirectory == NO)
    {
        [wk_manager createDirectoryAtPath:database_path withIntermediateDirectories:YES attributes:nil error:NULL];
    }
    return database_path;
}

+ (NSString *)getCrashLog{
    NSString *crashDirectoryPath=[YNCrashReport getDocumentWithName:CrashDirectory];
    NSString *crashFilePath=[crashDirectoryPath stringByAppendingPathComponent:CrashLogFileName];
    NSString *crashLog;
    NSError *error;
    crashLog=[[NSString alloc] initWithContentsOfFile:crashFilePath encoding:NSUTF8StringEncoding error:&error];
    if (!crashLog) {
        NSLog(@"CrashLog 出错:%@",error);
    }
    return crashLog;
}

+ (void)clearAllCrashLog{
    NSString *crashDirectoryPath=[YNCrashReport getDocumentWithName:CrashDirectory];
//    NSString *crashFilePath=[crashDirectoryPath stringByAppendingPathComponent:CrashLogFileName];
    NSError *error;
    BOOL result = [[NSFileManager defaultManager] removeItemAtPath:crashDirectoryPath error:&error];
    if (result) {
        NSLog(@"CrashLog清除成功");
    }else
    {
        NSLog(@"CrashLog清除失败:%@",error);
    }
}

+ (void)writeToFileWithDic:(NSDictionary *)dic{
    NSString *theStr=[dic description];
    NSString *crashDirectoryPath=[YNCrashReport getDocumentWithName:CrashDirectory];
    NSString *crashFilePath=[crashDirectoryPath stringByAppendingPathComponent:CrashLogFileName];
    NSError *error;
    BOOL result=[theStr writeToFile:crashFilePath atomically:YES encoding:NSUTF8StringEncoding error:&error];
    if (result) {
        NSLog(@"CrashLog写入文件成功");
    }else
    {
        NSLog(@"CrashLog写入文件失败:%@",error);
    }
}

+ (NSArray *)backtrace
{
    void* callstack[128];
    int frames = backtrace(callstack, 128);
    char **strs = backtrace_symbols(callstack, frames);
    
    int i;
    NSMutableArray *backtrace = [NSMutableArray arrayWithCapacity:frames];
    for (
         i = UncaughtExceptionHandlerSkipAddressCount;
         i < UncaughtExceptionHandlerSkipAddressCount +
         UncaughtExceptionHandlerReportAddressCount;
         i++)
    {
        [backtrace addObject:[NSString stringWithUTF8String:strs[i]]];
    }
    free(strs);
    
    return backtrace;
}

- (void)validateAndSaveCriticalApplicationData
{
    
}
@end

void HandleException(NSException *exception)
{
    int32_t exceptionCount = OSAtomicIncrement32(&UncaughtExceptionCount);
    if (exceptionCount > UncaughtExceptionMaximum)
    {
        return;
    }
    
    NSArray *callStack = [YNCrashReport backtrace];
    NSMutableDictionary *userInfo =
    [NSMutableDictionary dictionaryWithDictionary:[exception userInfo]];
    [userInfo
     setObject:callStack
     forKey:UncaughtExceptionHandlerAddressesKey];
    [YNCrashReport writeToFileWithDic:userInfo];
}

void SignalHandler(int signal)
{
    int32_t exceptionCount = OSAtomicIncrement32(&UncaughtExceptionCount);
    if (exceptionCount > UncaughtExceptionMaximum)
    {
        return;
    }
    
    NSMutableDictionary *userInfo =
    [NSMutableDictionary
     dictionaryWithObject:[NSNumber numberWithInt:signal]
     forKey:UncaughtExceptionHandlerSignalKey];
    
    NSArray *callStack = [YNCrashReport backtrace];
    [userInfo
     setObject:callStack
     forKey:UncaughtExceptionHandlerAddressesKey];
    [YNCrashReport writeToFileWithDic:userInfo];
}

void InstallUncaughtExceptionHandler(void)
{
    NSSetUncaughtExceptionHandler(&HandleException);
    signal(SIGABRT, SignalHandler);
    signal(SIGILL, SignalHandler);
    signal(SIGSEGV, SignalHandler);
    signal(SIGFPE, SignalHandler);
    signal(SIGBUS, SignalHandler);
    signal(SIGPIPE, SignalHandler);
}
